﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DAL.Repositories;
using System.Data;
using System.Transactions;
using System.Collections.ObjectModel;

namespace DAL.AbstractRepositories
{
    public class AbstractRepository<T> : IRepository<T> where T: class
    {
        #region properties

        private IList<T> inserted;
        protected IList<T> Inserted
        {
            get
            {

                if (inserted == null)
                    inserted = new List<T>();

                return inserted;
            }
        }

        private IList<T> updated;
        protected IList<T> Updated
        {
            get
            {

                if (updated == null)
                    updated = new List<T>();

                return updated;
            }
        }
        
        private IList<T> deleted;
        protected IList<T> Deleted
        { 
             get {

                if (deleted == null)
                    deleted = new List<T>();

                return deleted;
            } 
        }


        /// <summary>
        /// Solo intereza igualdad de referencia. 
        /// Es para poder saber cual es el id del objeto que esta en la lista despues de insertado 
        /// y poder tener muchas inserciones con muchas actiones afterSave.
        /// Tiene que ser la misma referencia de lo contrario no sirve. 
        /// </summary>
        /// 
        private IDictionary<T, long> idAutoGenerados;
        public IDictionary<T, long> IdAutoGenerados 
        {
            get 
            {
                if (idAutoGenerados == null)
                    idAutoGenerados = new Dictionary<T, long>();

                return idAutoGenerados;
            }
        }
        
        private Action actionBeforeSave;
        public Action ActionBeforeSave
        {
            set
            {
                if (value == null)
                    throw new ArgumentNullException();

                actionBeforeSave = value;

            }

            get { return actionBeforeSave; }
        }

        /// <summary>
        /// Al llamar a save se ejecuta y se pierde la referencia
        /// </summary>
        /// 
        private Action actionAfterSave;
        public Action ActionAfterSave
        {
            set
            {
                if (value == null)
                    throw new ArgumentNullException();

                actionAfterSave = value;
            }

            get { return actionAfterSave; }
        }

        #endregion properties

        public AbstractRepository() {

            ActionAfterSave = () => { };
            ActionBeforeSave = () => { };
        }
        public virtual void Insert(T entity)
        {
            throw new NotImplementedException ();
        }

        public virtual void Delete(T entity)
        {
            throw new NotImplementedException ();
        }

        public virtual void Update(T entity)
        {
            throw new NotImplementedException ();
        }

        public virtual IEnumerable<T> GetAll()
        {
            return null;

        }

        public virtual T GetById(int id)
        {
            return null;
        }

        public virtual void Save()
        {
            IdAutoGenerados.Clear();
            using (TransactionScope scope = new TransactionScope() ) {
                try
                {
                    
                        ActionBeforeSave();
                       
                    

                    foreach(T le in Inserted)
                        InsertEntity(le);

                    foreach(T le in Updated)
                        UpdateEntity(le);

                    foreach(T le in Deleted)
                        DeleteEntity(le);

                        ActionAfterSave();
                    

                    scope.Complete();
               
                }finally {
                    Clear();
                    ActionBeforeSave = () => { };
                    ActionAfterSave = () => { };
                }
            }
        }

        protected virtual void DeleteEntity(T le){}

        protected virtual void UpdateEntity(T le){}

        protected virtual void InsertEntity(T le){}

        public virtual void Clear()
        {
            if(inserted!=null)
                inserted.Clear();
            
            if(updated!=null)
                updated.Clear();
            
            if(deleted!=null)
                deleted.Clear();
        }

       
    }
    
    
    }

